package com.zjuh.goodboy.store.link;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;

import com.zjuh.goodboy.claw.link.Link;
import com.zjuh.goodboy.claw.link.LinkUtil;
import com.zjuh.goodboy.misc.iterator.ResultIterator;
import com.zjuh.goodboy.store.jdbc.BaseJdbcDaoSupport;
import com.zjuh.goodboy.util.SqlDateUtil;
import com.zjuh.goodboy.util.StringUtil;
import com.zjuh.splist.util.cache2.CacheResult;
import com.zjuh.splist.util.cache2.impl.SimpleRamCache;
import com.zjuh.sweet.sql.util.SqlUtil;

public class JdbcLinkManager extends BaseJdbcDaoSupport implements LinkManager {

	private String insertSql = "insert into goodboy_link (url, label, token, last_claw_date, gmt_create, gmt_modified) values(?,?,?, now(), now(), now())";
	
	private String selectSql = "select id, label, last_claw_date, url from goodboy_link where last_claw_date <= ? order by last_claw_date asc";
	
	private SimpleRamCache linkCache = new SimpleRamCache();
	
	private static final int NS_LINK_TOKEN = 1;
	
	private static final int LINK_CACHE_EXPIRE = 3600 * 24;
	
	public JdbcLinkManager() {
		super();
		linkCache.setNamespaces("1");
	}
	
	private boolean existFromCache(String token) {
		CacheResult cr = linkCache.get(NS_LINK_TOKEN, token);
		if (!cr.isSuccess()) {
			return false;
		}
		Object val = cr.getValue();
		if (val == null) {
			return false;
		}
		
		return ((Boolean)val).booleanValue();
	}

	protected boolean exist(String token) {
		if (existFromCache(token)) {
			return true;
		}
		String sql = "select token from goodboy_link where token = ?";
		Object dbToken = this.getJdbcTemplate().query(sql, new Object[] {token} , new ResultSetExtractor() {

			@Override
			public Object extractData(ResultSet rs) throws SQLException, DataAccessException {
				if (rs.next()) {
					return rs.getString("token");
				} else {
					return null;
				}
			}
		});
		boolean ret =  dbToken != null;
		
		linkCache.put(NS_LINK_TOKEN, token, ret, LINK_CACHE_EXPIRE);
		
		return ret;
	}
	
	@Override
	public ResultIterator<Link> canClawLinks(Date canClawDate) {
		Connection conn = null;
		PreparedStatement pstmt = null;
		try {
			conn  = getConnection();
			pstmt = conn.prepareStatement(selectSql);
			pstmt.setTimestamp(1, SqlDateUtil.asTimestamp(canClawDate));
			pstmt.setFetchSize(1);
			ResultSet rs = pstmt.executeQuery();
			return new ResultSetIterator(conn, pstmt, rs, this);
		} catch (SQLException e) {
			SqlUtil.close(pstmt);
			this.closeConnection(conn);
			throw new RuntimeException(e);
		}
	}
	
	private static class ResultSetIterator implements ResultIterator<Link> {

		private Connection conn;
		
		private PreparedStatement pstmt;
		
		private ResultSet rs;
		
		private BaseJdbcDaoSupport baseJdbcDaoSupport;
		
		public ResultSetIterator(Connection conn, PreparedStatement pstmt,	ResultSet rs, BaseJdbcDaoSupport baseJdbcDaoSupport) {
			super();
			this.conn = conn;
			this.pstmt = pstmt;
			this.rs = rs;
			this.baseJdbcDaoSupport = baseJdbcDaoSupport;
		}

		@Override
		public void close() {
			SqlUtil.close(rs);
			SqlUtil.close(pstmt);
			baseJdbcDaoSupport.closeConnection(conn);
		}

		@Override
		public boolean hasNext() {
			try {
				return rs.next();
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
		}

		@Override
		public Link next() {
			try {
				return LinkUtil.rsToLink(rs);
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
		}
		
	}
	
	public void updateLinkClawDate(long linkId) {
		this.getJdbcTemplate().update("update goodboy_link set last_claw_date = now(), gmt_modified = now() where id = ?", new Object[] { linkId });
	}
	
	public void saveLinks(final List<Link> links) {
		for (Link link : links) {
			if (exist(link.getToken())) {
				continue;
			}
			insert(link);
		}
	}
	
	private void insert(final Link link) {
		getJdbcTemplate().update(insertSql, new PreparedStatementSetter() {

			@Override
			public void setValues(PreparedStatement ps) throws SQLException {
				setLinkValues(ps, link);
			}
			
		} );
	}
	
	public void saveLink(final Link link) {
		saveLinks(Arrays.asList(link));
	}
	
	private void setLinkValues(PreparedStatement ps, Link link) throws SQLException{
		ps.setString(1, StringUtil.trimToLength(link.getUrl(), 1024));
		ps.setString(2, link.getLabel());
		ps.setString(3, link.getToken());
	}
	
}
